#pragma once
#include "PV2D.h"

namespace geometry {
class Segment2D
{
public:
	Segment2D(const Point2D& sptt, const Point2D& eptt)
    : spt(sptt), ept(eptt) 
  {
  }

	~Segment2D(void) { }

public:
	const Point2D& Start() const { return spt; }
	const Point2D& End() const { return ept; }
	Vector2D Direction() const { return ept-spt; }
	template<typename T> T Length() const { return spt.Distance<T>(ept); }
	template<typename T> T Distance(const Point2D& pt) const;
	template<typename T> T Distance2(const Point2D& pt) const;

public:
  bool PtNear(const Point2D& point, double tolerance) const
  {
    return Distance2<double>(point)<square(tolerance);
  }

private:
	const Point2D& spt;
	const Point2D& ept;
};

template<typename T> T Segment2D::Distance(const Point2D& pt) const
{
	Vector2D v = ept-spt;
	Vector2D w = pt-spt;
	double sw = w*v;
	if(sw<=0) {
		return w.Length<T>();
	}

	double sv = v*v;
	if(sv>0) {
		if(sv<=sw) {
			return pt.Distance<T>(ept);
		}

		Point2D ptb = spt+sw/sv*v;
		return pt.Distance<T>(ptb);
	} else {
		return w.Length<T>();
	}
}

template<typename T> T Segment2D::Distance2(const Point2D& pt) const
{
	Vector2D v = ept-spt;
	Vector2D w = pt-spt;
	double sw = w*v;
	if(sw<=0) {
		return w.Length2<T>();
	}

	double sv = v*v;
	if(sv>0) {
		if(sv<=sw) {
			return pt.Distance2<T>(ept);
		}

		Point2D ptb = spt+sw/sv*v;
		return pt.Distance2<T>(ptb);
	} else {
		return w.Length2<T>();
	}
}
}
